#Import de bibliotecas
import pandas as pd
import numpy as np
from datetime import datetime
import plotly.express as px
import plotly.graph_objects as go
import re
from statsmodels.tsa.seasonal import seasonal_decompose
import matplotlib.pyplot as plt
Aqui vou importar os dados direto do Github e começar a tratar as colunas. Primeiro vou tratar as colunas do tipo data e depois exibir o dataset.
url = 'https://raw.githubusercontent.com/jcbdoliveira/Bootcamp-Unimed-BH-Evolucao-COVID-19/main/covid_19_data.csv'
df = pd.read_csv(url, parse_dates=['ObservationDate', 'Last Update'])
df
| SNo | ObservationDate | Province/State | Country/Region | Last Update | Confirmed | Deaths | Recovered | |
|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 2020-01-22 | Anhui | Mainland China | 2020-01-22 17:00:00 | 1.0 | 0.0 | 0.0 |
| 1 | 2 | 2020-01-22 | Beijing | Mainland China | 2020-01-22 17:00:00 | 14.0 | 0.0 | 0.0 |
| 2 | 3 | 2020-01-22 | Chongqing | Mainland China | 2020-01-22 17:00:00 | 6.0 | 0.0 | 0.0 |
| 3 | 4 | 2020-01-22 | Fujian | Mainland China | 2020-01-22 17:00:00 | 1.0 | 0.0 | 0.0 |
| 4 | 5 | 2020-01-22 | Gansu | Mainland China | 2020-01-22 17:00:00 | 0.0 | 0.0 | 0.0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 26708 | 26709 | 2020-05-19 | Wyoming | US | 2020-05-20 02:32:19 | 776.0 | 10.0 | 0.0 |
| 26709 | 26710 | 2020-05-19 | Xinjiang | Mainland China | 2020-05-20 02:32:19 | 76.0 | 3.0 | 73.0 |
| 26710 | 26711 | 2020-05-19 | Yukon | Canada | 2020-05-20 02:32:19 | 11.0 | 0.0 | 11.0 |
| 26711 | 26712 | 2020-05-19 | Yunnan | Mainland China | 2020-05-20 02:32:19 | 185.0 | 2.0 | 183.0 |
| 26712 | 26713 | 2020-05-19 | Zhejiang | Mainland China | 2020-05-20 02:32:19 | 1268.0 | 1.0 | 1267.0 |
26713 rows × 8 columns
Conferindo para ver se os campos 'ObservationDate', 'Last Update' estão como datetime mesmo.
df.dtypes
SNo int64 ObservationDate datetime64[ns] Province/State object Country/Region object Last Update datetime64[ns] Confirmed float64 Deaths float64 Recovered float64 dtype: object
Agora vou aplicar boas praticas de exploração de dados em Python.
def corrigir_colunas(col_name):
return re.sub(r"[/| ]", "", col_name).lower()
df.columns = [corrigir_colunas(col) for col in df.columns]
df.head()
| sno | observationdate | provincestate | countryregion | lastupdate | confirmed | deaths | recovered | |
|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 2020-01-22 | Anhui | Mainland China | 2020-01-22 17:00:00 | 1.0 | 0.0 | 0.0 |
| 1 | 2 | 2020-01-22 | Beijing | Mainland China | 2020-01-22 17:00:00 | 14.0 | 0.0 | 0.0 |
| 2 | 3 | 2020-01-22 | Chongqing | Mainland China | 2020-01-22 17:00:00 | 6.0 | 0.0 | 0.0 |
| 3 | 4 | 2020-01-22 | Fujian | Mainland China | 2020-01-22 17:00:00 | 1.0 | 0.0 | 0.0 |
| 4 | 5 | 2020-01-22 | Gansu | Mainland China | 2020-01-22 17:00:00 | 0.0 | 0.0 | 0.0 |
Até aqui estava apenas carregando e tratando os dados. As análises vão iniciar agora.
Primeiro vou filtrar os dados do Brasil e apresentar uma amostra.
df_Brasil = df.loc[(df.countryregion == 'Brazil') & (df.confirmed > 0)]
df_Brasil.head()
| sno | observationdate | provincestate | countryregion | lastupdate | confirmed | deaths | recovered | |
|---|---|---|---|---|---|---|---|---|
| 2455 | 2456 | 2020-02-26 | NaN | Brazil | 2020-02-26 23:53:02 | 1.0 | 0.0 | 0.0 |
| 2559 | 2560 | 2020-02-27 | NaN | Brazil | 2020-02-26 23:53:02 | 1.0 | 0.0 | 0.0 |
| 2668 | 2669 | 2020-02-28 | NaN | Brazil | 2020-02-26 23:53:02 | 1.0 | 0.0 | 0.0 |
| 2776 | 2777 | 2020-02-29 | NaN | Brazil | 2020-02-29 21:03:05 | 2.0 | 0.0 | 0.0 |
| 2903 | 2904 | 2020-03-01 | NaN | Brazil | 2020-02-29 21:03:05 | 2.0 | 0.0 | 0.0 |
Vamos visualizar os dados em gráfico
px.line(df_Brasil, 'observationdate', 'confirmed',
labels={'observationdate':'Data', 'confirmed':'Número de casos confirmados'},
title='Casos confirmados')
fig = go.Figure()
fig.add_trace(
go.Scatter(x=df_Brasil.observationdate, y=df_Brasil.deaths, name='Mortes', mode='lines+markers',
line=dict(color='red'))
)
fig.update_layout(title='Mortes por COVID-19 no Brasil',
xaxis_title='Data',
yaxis_title='Número de mortes')
fig.show()
fig = go.Figure()
fig.add_trace(
go.Scatter(x=df_Brasil.observationdate, y=df_Brasil.recovered, name='Mortes', mode='lines+markers',
line=dict(color='orange'))
)
fig.update_layout(title='Recuperados',
xaxis_title='Data',
yaxis_title='Casos recuperados')
fig.show()
Para obter o número de novo casos vou criar uma nova coluna para analisar os casos de hoje menos os de ontem, desta forma podemos avaliar a evolução da doença.
df_Brasil['casosdia'] = list(map(
lambda x: 0 if (x==0) else df_Brasil['confirmed'].iloc[x] - df_Brasil['confirmed'].iloc[x-1],
np.arange(df_Brasil.shape[0])
))
fig = go.Figure()
fig.add_trace(
go.Scatter(x=df_Brasil.observationdate, y=df_Brasil.casosdia, name='Novos casos', mode='lines+markers',
line=dict(color='black'))
)
fig.update_layout(title='Novos casos por dia',
xaxis_title='Data',
yaxis_title='Novos casos')
fig.show()
def taxa_crescimento(data, variable, data_inicial=None, data_final=None):
# Se data_inicial igual a None, define como a primeira data disponível no dataset
if data_inicial == None:
data_inicial = data.observationdate.loc[data[variable] > 0].min()
else:
data_inicial = pd.to_datetime(data_inicial)
# Se data_final igual a None, define como a ultima data disponível no dataset
if data_final == None:
data_final = data.observationdate.iloc[-1]
else:
data_final = pd.to_datetime(data_final)
# Define os valores de presente e passado
valor_passado = data.loc[data.observationdate == data_inicial, variable].values[0]
valor_presente = data.loc[data.observationdate == data_final, variable].values[0]
# Define o número de pontos no tempo q vamos avaliar
n = (data_final - data_inicial).days
_data_ini_taxa = data_inicial
_data_fin_taxa = data_final
# Calcula a taxa
taxa = (valor_presente/valor_passado)**(1/n) - 1
return taxa * 100
crescimento_medio = taxa_crescimento(df_Brasil, 'confirmed')
print(f"O crescimento médio do COVID-19 no Brasil no período avaliado foi de {crescimento_medio.round(2)}%.")
O crescimento médio do COVID-19 no Brasil no período avaliado foi de 16.27%.
Agora, vamos observar o comportamento da taxa de crescimento no tempo. Para isso, vamos definir uma função para calcular a taxa de crescimento diária e apresentar um gráfico de linha.
def taxa_crescimento_diaria(data, variable, data_inicio=None):
if data_inicio == None:
data_inicio = data.observationdate.loc[data[variable] > 0].min()
else:
data_inicio = pd.to_datetime(data_inicio)
data_fim = data.observationdate.max()
n = (data_fim - data_inicio).days
taxas = list(map(
lambda x: (data[variable].iloc[x] - data[variable].iloc[x-1]) / data[variable].iloc[x-1],
range(1,n+1)
))
return np.array(taxas)*100
tx_dia = taxa_crescimento_diaria(df_Brasil, 'confirmed')
tx_dia
array([ 0. , 0. , 100. , 0. ,
0. , 0. , 100. , 0. ,
225. , 0. , 53.84615385, 25. ,
24. , 22.58064516, 36.84210526, 190.38461538,
0. , 7.28476821, 23.45679012, 60.5 ,
15.88785047, 66.93548387, 27.69726248, 28.75157629,
51.4201763 , 24.45019405, 16.78794179, 13.66266133,
16.87548943, 14.47236181, 14.25226807, 9.01639344,
7.58928571, 24.8525879 , 19.57320273, 17.67115272,
12.58080557, 14.39929329, 7.43243243, 9.26325247,
15.40169394, 15.22017956, 11.88620903, 8.54521335,
5.54537122, 7.06807546, 5.57858688, 7.81903542,
12.10513815, 7.4329096 , 10.70501233, 8.83557983,
5.44492335, 5.4043566 , 5.73350023, 6.21648599,
9.35157462, 8.00823407, 9.77184834, 6.36504619,
6.88748019, 8.58316283, 8.80726429, 9.41456987,
5.75200431, 5.31224919, 4.86714727, 6.67216624,
6.29257964, 9.66263912, 7.23633807, 8.19087742,
6.24055441, 4.25346499, 4.23788714, 5.08272698,
6.69027125, 6.85190152, 8.42960156, 6.00115302,
3.24138906, 5.92666335, 6.4679208 ])
primeiro_dia = df_Brasil.observationdate.loc[df_Brasil.confirmed > 0].min()
fig = px.line(x=pd.date_range(primeiro_dia, df_Brasil.observationdate.max())[1:],
y=tx_dia, title='Taxa de crescimento de casos confirmados no Brasil',
labels={'y':'Taxa de crescimento', 'x':'Data'})
fig.show()
Vamos construir um modelo de séries temporais para prever os novos casos. Antes analisaremos a série temporal.
novoscasos = df_Brasil.casosdia
novoscasos.index = df_Brasil.observationdate
#A função seasonal_decompose usa o método de médias móveis para estimar a tendência.
res = seasonal_decompose(novoscasos)
fig, (ax1,ax2,ax3, ax4) = plt.subplots(4, 1,figsize=(10,8))
ax1.plot(res.observed)
ax2.plot(res.trend)
ax3.plot(res.seasonal)
ax4.scatter(novoscasos.index, res.resid)
ax4.axhline(0, linestyle='dashed', c='black')
plt.show()
Aqui os casos confoirmados foram decompostos em:
Observados (observed) Tendência (trend) Sasonalidade (seasonal) Ruído (resid)
confirmados = df_Brasil.confirmed
confirmados.index = df_Brasil.observationdate
res2 = seasonal_decompose(confirmados)
fig, (ax1,ax2,ax3, ax4) = plt.subplots(4, 1,figsize=(10,8))
ax1.plot(res2.observed)
ax2.plot(res2.trend)
ax3.plot(res2.seasonal)
ax4.scatter(confirmados.index, res2.resid)
ax4.axhline(0, linestyle='dashed', c='black')
plt.show()
ARIMA é um modelo auto-regressivo integrado de médias móveis.
Os modelos são ajustados aos dados da série temporal para entender melhor os dados ou para prever pontos futuros na série.
from pmdarima.arima import auto_arima
modelo = auto_arima(confirmados)
pd.date_range('2020-05-01', '2020-05-19')
DatetimeIndex(['2020-05-01', '2020-05-02', '2020-05-03', '2020-05-04',
'2020-05-05', '2020-05-06', '2020-05-07', '2020-05-08',
'2020-05-09', '2020-05-10', '2020-05-11', '2020-05-12',
'2020-05-13', '2020-05-14', '2020-05-15', '2020-05-16',
'2020-05-17', '2020-05-18', '2020-05-19'],
dtype='datetime64[ns]', freq='D')
fig = go.Figure(go.Scatter(
x=confirmados.index, y=confirmados, name='Observado'
))
fig.add_trace(go.Scatter(x=confirmados.index, y = modelo.predict_in_sample(), name='Modelo ML'))
fig.add_trace(go.Scatter(x=pd.date_range('2020-05-20', '2020-05-30'), y=modelo.predict(10), name='Previsão ML'))
fig.update_layout(title='Previsão de casos confirmados para os próximos 10 dias', yaxis_title='Casos confirmados', xaxis_title='Data')
fig.show()
fig = go.Figure(go.Scatter(
x=confirmados.index, y=confirmados, name='Observado'
))
fig.add_trace(go.Scatter(x=confirmados.index, y = modelo.predict_in_sample(), name='Modelo ML'))
fig.add_trace(go.Scatter(x=pd.date_range('2020-05-20', '2020-06-05'), y=modelo.predict(15), name='Previsão ML'))
fig.update_layout(title='Previsão de casos confirmados para os próximos 15 dias', yaxis_title='Casos confirmados', xaxis_title='Data')
fig.show()
fig = go.Figure(go.Scatter(
x=confirmados.index, y=confirmados, name='Observado'
))
fig.add_trace(go.Scatter(x=confirmados.index, y = modelo.predict_in_sample(), name='Modelo ML'))
fig.add_trace(go.Scatter(x=pd.date_range('2020-05-20', '2020-06-20'), y=modelo.predict(30), name='Previsão ML'))
fig.update_layout(title='Previsão de casos confirmados para os próximos 30 dias', yaxis_title='Casos confirmados', xaxis_title='Data')
fig.show()
O Prophet é um procedimento para prever dados de séries temporais com base em um modelo aditivo em que as tendências não lineares são ajustadas à sazonalidade anual, semanal e diária, além dos efeitos de feriados. Funciona melhor com séries temporais com fortes efeitos sazonais e várias temporadas de dados históricos.
!conda install -c conda-forge fbprophet -y
^C
from fbprophet import Prophet
# preparando os dados
train = confirmados.reset_index()[:-5]
test = confirmados.reset_index()[-5:]
# renomeia colunas
train.rename(columns={"observationdate":"ds","confirmed":"y"},inplace=True)
test.rename(columns={"observationdate":"ds","confirmed":"y"},inplace=True)
test = test.set_index("ds")
test = test['y']
profeta = Prophet(growth="logistic", changepoints=['2020-03-21', '2020-03-30', '2020-04-25', '2020-05-03', '2020-05-10'])
#pop = 1000000
pop = 211463256 #https://www.ibge.gov.br/apps/populacao/projecao/box_popclock.php
train['cap'] = pop
# Treina o modelo
profeta.fit(train)
# Construindo previsões para o futuro
future_dates = profeta.make_future_dataframe(periods=200)
future_dates['cap'] = pop
forecast = profeta.predict(future_dates)
fig = go.Figure()
fig.add_trace(go.Scatter(x=forecast.ds, y=forecast.yhat, name='Predição'))
fig.add_trace(go.Scatter(x=test.index, y=test, name='Observados - Teste'))
fig.add_trace(go.Scatter(x=train.ds, y=train.y, name='Observados - Treino'))
fig.update_layout(title='Predições de casos confirmados no Brasil')
fig.show()